package com.xiaotu.handler;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaotu.cache.BattleCache;
import com.xiaotu.chessAI.AI;
import com.xiaotu.dto.CoordinateAIDto;
import com.xiaotu.dto.CoordinateDto;
import com.xiaotu.dto.TrapCoordinateDto;
import com.xiaotu.dto.WinnerOperationDto;
import com.xiaotu.service.impl.BattleHandlerServiceImpl;
import com.xiaotu.service.redission.RedissionService;
import com.xiaotu.service.redission.impl.RedissionServiceImpl;
import com.xiaotu.util.SpringContext;
import com.xiaotu.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.websocket.Session;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zhangxiao
 * @create 2021-10-18 16:12
 */
@Slf4j
public abstract class BasicHandler {

    /**
     * 缓存对战玩家
     */
    protected BattleCache battleCache = SpringContext.getBean(BattleCache.class);

    /**
     * 步数
     */
    protected AtomicInteger step = new AtomicInteger(1);

    private RedissionService redissionService = SpringContext.getBean(RedissionServiceImpl.class);

    protected void saveCoordinate(CoordinateDto coordinateDto) {
        coordinateDto.setStep(step.getAndAdd(1));
        SpringUtil.getBean("battleHandlerServiceImpl", BattleHandlerServiceImpl.class).saveCoordinate(coordinateDto);
    }

    /**
     * 走棋消息
     */
    protected void transferCoordinateMessage(Session session, String message) throws JsonProcessingException {
        CoordinateDto coordinateDto = JSONUtil.toBean(message, CoordinateDto.class);
        CoordinateAIDto coordinateAIDto = JSONUtil.toBean(message, CoordinateAIDto.class);
        Integer playerId = (Integer) session.getUserProperties().get("playerId");
        coordinateDto.setPlayerId(playerId);
        //保存每步记录 --待优化 可以存储的缓存中  异步消费存储
        saveCoordinate(coordinateDto);
        if (coordinateDto.getMode() == 1) {
            //调用AI
            ObjectMapper objectMapper = new ObjectMapper();
            coordinateAIDto.setChessAI(AI.getAICoordinate(coordinateAIDto.getChessAI()));
            push(objectMapper.writeValueAsString(coordinateAIDto), session);
        } else if (coordinateDto.getMode() == 2) {
            Session anotherPlayer = battleCache.getAnotherPlayer(coordinateDto.getRoomId(), session);
            if (ObjectUtil.isNotNull(anotherPlayer)) {
                push(message, anotherPlayer);
            } else {
                throw new RuntimeException("对方已离开房间");
            }
        }
    }

    /**
     * 陷阱消息
     */
    protected void transferTrapCoordinateMessage(String message, Session client) {
        TrapCoordinateDto trapCoordinateDto = JSONUtil.toBean(message, TrapCoordinateDto.class);
        if (trapCoordinateDto.getMode() == 2) {
            Session anotherPlayer = battleCache.getAnotherPlayer(trapCoordinateDto.getRoomId(), client);
            if (ObjectUtil.isNotNull(anotherPlayer)) {
                push(message, anotherPlayer);
            } else {
                throw new RuntimeException("对方已离开房间");
            }
        }
    }

    /**
     * 正常比赛胜利之后的消息
     */
    protected void winnerGameAfterMessage(Session session, String message) {
        WinnerOperationDto winnerOperationDto = JSONUtil.toBean(message, WinnerOperationDto.class);
        session.getUserProperties().put("winnerOperationDto", JSONUtil.toJsonStr(winnerOperationDto));
        Session anotherPlayer = battleCache.getAnotherPlayer(winnerOperationDto.getRoomId(), session);
        if (ObjectUtil.isNotNull(anotherPlayer)) {
            if (winnerOperationDto.getOperation() == 2) {
                push("结束比赛", session);
                push("结束比赛", anotherPlayer);
            } else {
                if (ObjectUtil.isNotNull(anotherPlayer.getUserProperties().get("winnerOperationDto"))) {
                    WinnerOperationDto anotherWinnerOperation = JSONUtil.toBean(anotherPlayer.getUserProperties().get("winnerOperationDto").toString(), WinnerOperationDto.class);
                    if (anotherWinnerOperation.getOperation() == 1 && winnerOperationDto.getOperation() == 1) {
                        push("继续比赛", session);
                        push("继续比赛", anotherPlayer);
                        //清除
                        removeUserPropertiesKey(session, "winnerOperationDto");
                        removeUserPropertiesKey(anotherPlayer, "winnerOperationDto");
                    }
                }
            }
        } else {
            throw new RuntimeException("对方已离开房间");
        }
    }

    protected boolean validOldToken(String phone, String oldToken) {
        return redissionService.validPhoneToken(phone, oldToken);
    }

    protected void oldTokenInvalidMessage(Integer roomId, Session currentPlayer) {
        push("您已被挤出登录", currentPlayer);
    }

    protected void noticeAnotherPlayer(Integer roomId, Session client, String message) {
        Session anotherPlayer = battleCache.getAnotherPlayer(roomId, client);
        if (ObjectUtil.isNull(anotherPlayer)) {
            //case:人机
            if (!message.equals("对方已认输")) {
                /**
                 * 有可能调整
                 * 和棋规则：前30步 拒绝求和 30 步之后同意求和
                 */
                if (message.equals("对方发起求和")) {
                    if (step.get() <= 30) {
                        push("拒绝求和", client);
                    } else {
                        push("同意求和", client);
                    }
                } else {
                    push("拒绝求和", client);
                }
            }
        } else {
            //case:人人
            push(message, anotherPlayer);
        }
    }

    protected void gameAgainReq(Integer roomId, Session current, String message) {
        BattleHandlerServiceImpl battleHandlerService = SpringUtil.getBean("battleHandlerServiceImpl", BattleHandlerServiceImpl.class);
        //校验是否同意再来一局，是：则清空历史走棋记录
        if (message.equals("同意再来一局"))
            battleHandlerService.clearHistoryCoordinate(roomId);
        //校验对方是否足够入场金币 100
        Session anotherPlayer = battleCache.getAnotherPlayer(roomId, current);
        if (ObjectUtil.isNotNull(anotherPlayer)) {
            Integer anotherPlayerId = Integer.valueOf(anotherPlayer.getUserProperties().get("playerId").toString());
            boolean admissionAllowed = battleHandlerService.isAdmissionAllowed(anotherPlayerId);
            //足够的入场金币
            if (admissionAllowed) {
                push(message, anotherPlayer);
            } else {
                push("拒绝再来一局", current);
            }
        } else {
            push("拒绝再来一局", current);
        }
    }

    /**
     * 人机对战
     *
     * @param roomId
     */
    protected void manMachineReq(Integer roomId) {
        battleCache.joinRoom(roomId, 2, null);
    }


    private void removeUserPropertiesKey(Session battleHandler, String key) {
        battleHandler.getUserProperties().remove(key);
    }

    /**
     * 推送消息
     *
     * @param message
     * @param session
     */
    public void push(String message, Session session) {
        if (StringUtils.isEmpty(message)) {
            throw new RuntimeException("message must not be null, message=" + message);
        }
        if (!session.isOpen()) {
            this.close(session);
            throw new RuntimeException("session is closed, message=" + message);
        }
        //发送消息
        this.sendMessage(message, session);
    }

    /**
     * 推送消息给客户端
     *
     * @param message 消息内容
     */
    private void sendMessage(String message, Session session) {
        try {
            //使用同步块和同步方法发送。看是否能防止产生IllegalStateException异常
            //modify by caoshuo at 200506
            synchronized (this) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            log.error("websocket连接发送客户端发送消息时异常！{}-{}", message, this.toString(), e);
        }
    }

    /**
     * 关闭session连接
     */
    protected void close(Session session) {
        if (session == null) {
            log.debug("websocket连接关闭完成。{}", this.toString());
            return;
        }
        try {
            if (session.isOpen()) {
                session.close();
            }
            log.info("连接已经关闭");
        } catch (IOException e) {
            log.error("websocket连接关闭时异常。{}", this.toString(), e);
        }
    }

    protected void noticeAnotherPlayer(Session session) {
        Session anotherPlayer = battleCache.getAnotherPlayer((Integer) session.getUserProperties().get("roomId"), session);
        if (ObjectUtil.isNotNull(anotherPlayer))
            push("对方已离开房间", anotherPlayer);
    }

}
